import sys
import time
import random
from ctypes import *
from datetime import datetime

# Configuration - Set this to False if you don't have hardware
USE_HARDWARE = False  # Set to True when you have actual hardware

# Load the DLL only if using hardware
if USE_HARDWARE:
    try:
        owl429 = WinDLL("Owl429.dll")
        print("✅ Owl429.dll loaded successfully")
        DLL_LOADED = True
    except Exception as e:
        print(f"❌ DLL load failed: {e}")
        sys.exit(1)
else:
    print("🔶 Running in EMULATION MODE (no hardware required)")
    DLL_LOADED = False

# Constants
OWL429_OK = 0
MAX_STRING_LEN = 256

# Channel mapping
CHANNEL_RADIO_BUS = 1  # TX
CHANNEL_VHF_BUS = 2    # RX
CHANNEL_NAV_BUS = 3    # RX
CHANNEL_UHF_BUS = 4    # RX
CHANNEL_AMUTX_BUS = 5  # TX
CHANNEL_AMURX_BUS = 6  # RX

# Emulation data
EMULATION_DATA = {
    CHANNEL_VHF_BUS: [
        {"label": 10, "data": 0x11111, "description": "VHF1 Frequency"},
        {"label": 11, "data": 0x22222, "description": "VHF2 Frequency"},
        {"label": 12, "data": 0x33333, "description": "VHF3 Frequency"},
    ],
    CHANNEL_NAV_BUS: [
        {"label": 20, "data": 0x44444, "description": "NAV1 Course"},
        {"label": 21, "data": 0x55555, "description": "NAV2 Course"},
        {"label": 22, "data": 0x66666, "description": "ILS Frequency"},
    ],
    CHANNEL_UHF_BUS: [
        {"label": 30, "data": 0x77777, "description": "UHF1 Frequency"},
        {"label": 31, "data": 0x88888, "description": "UHF2 Frequency"},
    ],
    CHANNEL_AMURX_BUS: [
        {"label": 40, "data": 0x99999, "description": "AMU Data 1"},
        {"label": 41, "data": 0xAAAAA, "description": "AMU Data 2"},
        {"label": 42, "data": 0xBBBBB, "description": "AMU Data 3"},
    ]
}

class Owl429TimeTag(Structure):
    _fields_ = [
        ("hour", c_uint32),
        ("minute", c_uint32),
        ("second", c_uint32),
        ("microSecond", c_uint32)
    ]

class hOwl429IBoardFactory(Structure):
    _fields_ = [("_handle", c_void_p)]
    
class hOwl429IBoard(Structure):
    _fields_ = [("_handle", c_void_p)]
    
class hOwl429TxScheduledLabelConfig(Structure):
    _fields_ = [("_handle", c_void_p)]
    
class hOwl429TxRateOrientedConfig(Structure):
    _fields_ = [("_handle", c_void_p)]
    
class hOwl429RxChronMonConfig(Structure):
    _fields_ = [("_handle", c_void_p)]
    
class hOwl429ITxRateOrientedChannel(Structure):
    _fields_ = [("_handle", c_void_p)]
    
class hOwl429IRxChronMonChannel(Structure):
    _fields_ = [("_handle", c_void_p)]
    
class hOwl429ReadBuffer(Structure):
    _fields_ = [("_handle", c_void_p)]
    
class hOwl429IBufferEntry(Structure):
    _fields_ = [("_handle", c_void_p)]

def get_error_message():
    """Get the last error message from the DLL"""
    if not DLL_LOADED:
        return "Emulation mode - no hardware errors"
    
    error_text = create_string_buffer(MAX_STRING_LEN)
    owl429.Owl429_ErrorHandler_get(error_text, MAX_STRING_LEN)
    return error_text.value.decode()

def setup_function_prototypes():
    """Setup all the function prototypes for the DLL"""
    if not DLL_LOADED:
        return  # Skip in emulation mode
    
    # Factory functions
    owl429.Owl429_IBoardFactory_localBoardFactory.argtypes = [POINTER(hOwl429IBoardFactory)]
    owl429.Owl429_IBoardFactory_localBoardFactory.restype = c_int

    owl429.Owl429_IBoardFactory_getSerialNumbers.argtypes = [hOwl429IBoardFactory, POINTER(c_uint64), POINTER(c_uint32)]
    owl429.Owl429_IBoardFactory_getSerialNumbers.restype = c_int

    owl429.Owl429_IBoardFactory_createBoard.argtypes = [hOwl429IBoardFactory, c_uint64, POINTER(hOwl429IBoard)]
    owl429.Owl429_IBoardFactory_createBoard.restype = c_int

    # Configuration functions
    owl429.Owl429_TxScheduledLabelConfig_new.argtypes = [POINTER(hOwl429TxScheduledLabelConfig), c_uint8]
    owl429.Owl429_TxScheduledLabelConfig_new.restype = c_int

    owl429.Owl429_TxScheduledLabelConfig_setTransferPeriod.argtypes = [hOwl429TxScheduledLabelConfig, c_uint32]
    owl429.Owl429_TxScheduledLabelConfig_setTransferPeriod.restype = c_int

    owl429.Owl429_TxScheduledLabelConfig_setLabel.argtypes = [hOwl429TxScheduledLabelConfig, c_uint8]
    owl429.Owl429_TxScheduledLabelConfig_setLabel.restype = c_int

    owl429.Owl429_ArincUtil_setSsm.argtypes = [c_uint32, c_uint32, POINTER(c_uint32)]
    owl429.Owl429_ArincUtil_setSsm.restype = c_int

    owl429.Owl429_ArincUtil_setUnsignedBnrData.argtypes = [c_uint32, c_uint32, POINTER(c_uint32)]
    owl429.Owl429_ArincUtil_setUnsignedBnrData.restype = c_int

    owl429.Owl429_TxScheduledLabelConfig_setDatum.argtypes = [hOwl429TxScheduledLabelConfig, c_uint32, c_int]
    owl429.Owl429_TxScheduledLabelConfig_setDatum.restype = c_int

    owl429.Owl429_TxRateOrientedConfig_new.argtypes = [POINTER(hOwl429TxRateOrientedConfig)]
    owl429.Owl429_TxRateOrientedConfig_new.restype = c_int

    owl429.Owl429_TxRateOrientedConfig_addTransferScheduled.argtypes = [
        hOwl429TxRateOrientedConfig,
        hOwl429TxScheduledLabelConfig,
        POINTER(c_uint32)
    ]
    owl429.Owl429_TxRateOrientedConfig_addTransferScheduled.restype = c_int

    owl429.Owl429_TxRateOrientedConfig_setSpeed.argtypes = [hOwl429TxRateOrientedConfig, c_uint32]
    owl429.Owl429_TxRateOrientedConfig_setSpeed.restype = c_int

    owl429.Owl429_RxChronMonConfig_new.argtypes = [POINTER(hOwl429RxChronMonConfig)]
    owl429.Owl429_RxChronMonConfig_new.restype = c_int

    owl429.Owl429_RxChronMonConfig_setSpeed.argtypes = [hOwl429RxChronMonConfig, c_uint32]
    owl429.Owl429_RxChronMonConfig_setSpeed.restype = c_int

    # Channel configuration functions
    owl429.Owl429_IBoard_configureChannelTxRate.argtypes = [
        hOwl429IBoard,
        c_uint8,
        hOwl429TxRateOrientedConfig,
        POINTER(hOwl429ITxRateOrientedChannel)
    ]
    owl429.Owl429_IBoard_configureChannelTxRate.restype = c_int

    owl429.Owl429_IBoard_configureChannelRx.argtypes = [
        hOwl429IBoard,
        c_uint8,
        hOwl429RxChronMonConfig,
        POINTER(hOwl429IRxChronMonChannel)
    ]
    owl429.Owl429_IBoard_configureChannelRx.restype = c_int

    owl429.Owl429_IBoard_configureChannelTxRx.argtypes = [
        hOwl429IBoard,
        c_uint8,
        hOwl429TxRateOrientedConfig,
        hOwl429RxChronMonConfig,
        POINTER(hOwl429ITxRateOrientedChannel)
    ]
    owl429.Owl429_IBoard_configureChannelTxRx.restype = c_int

    owl429.Owl429_ITxRateOrientedChannel_getMonitor.argtypes = [
        hOwl429ITxRateOrientedChannel,
        POINTER(hOwl429IRxChronMonChannel)
    ]
    owl429.Owl429_ITxRateOrientedChannel_getMonitor.restype = c_int

    # Start/Stop functions
    owl429.Owl429_IRxChronMonChannel_start.argtypes = [hOwl429IRxChronMonChannel]
    owl429.Owl429_IRxChronMonChannel_start.restype = c_int

    owl429.Owl429_ITxRateOrientedChannel_start.argtypes = [hOwl429ITxRateOrientedChannel]
    owl429.Owl429_ITxRateOrientedChannel_start.restype = c_int

    # Reading functions
    owl429.Owl429_ReadBuffer_new.argtypes = [POINTER(hOwl429ReadBuffer)]
    owl429.Owl429_ReadBuffer_new.restype = c_int

    owl429.Owl429_ReadBuffer_size.argtypes = [hOwl429ReadBuffer, POINTER(c_uint32)]
    owl429.Owl429_ReadBuffer_size.restype = c_int

    owl429.Owl429_ReadBuffer_getEntry.argtypes = [hOwl429ReadBuffer, c_uint32, POINTER(hOwl429IBufferEntry)]
    owl429.Owl429_ReadBuffer_getEntry.restype = c_int

    owl429.Owl429_IBufferEntry_getArincDataWord.argtypes = [hOwl429IBufferEntry, POINTER(c_uint32)]
    owl429.Owl429_IBufferEntry_getArincDataWord.restype = c_int

    owl429.Owl429_IBufferEntry_getTimeTag.argtypes = [hOwl429IBufferEntry, POINTER(Owl429TimeTag)]
    owl429.Owl429_IBufferEntry_getTimeTag.restype = c_int

    # Error handling
    owl429.Owl429_ErrorHandler_get.argtypes = [c_char_p, c_uint32]
    owl429.Owl429_ErrorHandler_get.restype = c_int

    # Cleanup functions
    owl429.Owl429_TxRateOrientedConfig_delete.argtypes = [POINTER(hOwl429TxRateOrientedConfig)]
    owl429.Owl429_TxRateOrientedConfig_delete.restype = c_int

    owl429.Owl429_RxChronMonConfig_delete.argtypes = [POINTER(hOwl429RxChronMonConfig)]
    owl429.Owl429_RxChronMonConfig_delete.restype = c_int

    owl429.Owl429_TxScheduledLabelConfig_delete.argtypes = [POINTER(hOwl429TxScheduledLabelConfig)]
    owl429.Owl429_TxScheduledLabelConfig_delete.restype = c_int

    owl429.Owl429_ReadBuffer_delete.argtypes = [POINTER(hOwl429ReadBuffer)]
    owl429.Owl429_ReadBuffer_delete.restype = c_int

    owl429.Owl429_ITxRateOrientedChannel_stop.argtypes = [hOwl429ITxRateOrientedChannel]
    owl429.Owl429_ITxRateOrientedChannel_stop.restype = c_int

    owl429.Owl429_IRxChronMonChannel_stop.argtypes = [hOwl429IRxChronMonChannel]
    owl429.Owl429_IRxChronMonChannel_stop.restype = c_int

    owl429.Owl429_IBoard_release.argtypes = [POINTER(hOwl429IBoard)]
    owl429.Owl429_IBoard_release.restype = c_int

    owl429.Owl429_IBoardFactory_release.argtypes = [POINTER(hOwl429IBoardFactory)]
    owl429.Owl429_IBoardFactory_release.restype = c_int

def test_arinc429_device():
    """Test function for ARINC-429 device"""
    print("=== ARINC-429 USB429 Device Test ===")
    print(f"Mode: {'HARDWARE' if USE_HARDWARE else 'EMULATION'}")
    print("This test will:")
    print("1. Initialize the device")
    print("2. Configure TX channels (RADIO_BUS and AMUTX_BUS)")
    print("3. Configure RX channels (VHF_BUS, NAV_BUS, UHF_BUS, AMURX_BUS)")
    print("4. Start all channels")
    print("5. Read data from RX channels for 10 seconds")
    print("6. Clean up resources")
    print()
    
    # Setup function prototypes (only in hardware mode)
    if USE_HARDWARE:
        setup_function_prototypes()
    
    # Initialize handles
    factory = hOwl429IBoardFactory()
    board = hOwl429IBoard()
    tx_channels = {}
    rx_channels = {}
    
    try:
        # Step 1: Initialize board factory
        print("1. Initializing board factory...")
        if USE_HARDWARE:
            rc = owl429.Owl429_IBoardFactory_localBoardFactory(byref(factory))
            if rc != OWL429_OK:
                print(f"❌ Factory init failed: {get_error_message()}")
                return False
            print("✅ Board factory initialized")
        else:
            print("✅ Board factory initialized (emulated)")
        
        # Step 2: Get serial numbers
        print("2. Getting board serial numbers...")
        if USE_HARDWARE:
            serial_numbers = (c_uint64 * 10)()
            sn_size = c_uint32(10)
            rc = owl429.Owl429_IBoardFactory_getSerialNumbers(factory, serial_numbers, byref(sn_size))
            if rc != OWL429_OK or sn_size.value == 0:
                print(f"❌ No boards detected: {get_error_message()}")
                return False
            print(f"✅ Found board with S/N: {serial_numbers[0]}")
        else:
            print("✅ Found emulated board with S/N: 1234567890")
        
        # Step 3: Create board
        print("3. Creating board...")
        if USE_HARDWARE:
            rc = owl429.Owl429_IBoardFactory_createBoard(factory, 1234567890, byref(board))
            if rc != OWL429_OK:
                print(f"❌ Board creation failed: {get_error_message()}")
                return False
            print("✅ Board created successfully")
        else:
            print("✅ Board created successfully (emulated)")
        
        # Step 4: Configure TX channels
        print("4. Configuring TX channels...")
        
        # Configure RADIO_BUS (Channel 1)
        print("   Configuring RADIO_BUS (Channel 1)...")
        radio_config = configure_tx_channel(board, CHANNEL_RADIO_BUS, 10, 0x11111, 10)
        if radio_config:
            tx_channels[CHANNEL_RADIO_BUS] = radio_config
            print("   ✅ RADIO_BUS configured")
        else:
            print("   ❌ RADIO_BUS configuration failed")
            return False
        
        # Configure AMUTX_BUS (Channel 5)
        print("   Configuring AMUTX_BUS (Channel 5)...")
        amutx_config = configure_tx_channel(board, CHANNEL_AMUTX_BUS, 20, 0x22222, 5)
        if amutx_config:
            tx_channels[CHANNEL_AMUTX_BUS] = amutx_config
            print("   ✅ AMUTX_BUS configured")
        else:
            print("   ❌ AMUTX_BUS configuration failed")
            return False
        
        # Step 5: Configure RX channels
        print("5. Configuring RX channels...")
        
        rx_channels_config = {}
        for channel in [CHANNEL_VHF_BUS, CHANNEL_NAV_BUS, CHANNEL_UHF_BUS, CHANNEL_AMURX_BUS]:
            print(f"   Configuring Channel {channel}...")
            rx_config = configure_rx_channel(board, channel)
            if rx_config:
                rx_channels[channel] = rx_config
                rx_channels_config[channel] = rx_config
                print(f"   ✅ Channel {channel} configured")
            else:
                print(f"   ❌ Channel {channel} configuration failed")
                return False
        
        # Step 6: Start all channels
        print("6. Starting all channels...")
        
        # Start RX channels first
        for channel, config in rx_channels_config.items():
            if USE_HARDWARE:
                rc = owl429.Owl429_IRxChronMonChannel_start(config['handle'])
                if rc != OWL429_OK:
                    print(f"❌ Failed to start RX channel {channel}: {get_error_message()}")
                    return False
            print(f"✅ RX Channel {channel} started")
        
        # Start TX channels
        for channel, config in tx_channels.items():
            if USE_HARDWARE:
                rc = owl429.Owl429_ITxRateOrientedChannel_start(config['handle'])
                if rc != OWL429_OK:
                    print(f"❌ Failed to start TX channel {channel}: {get_error_message()}")
                    return False
            print(f"✅ TX Channel {channel} started")
        
        print("✅ All channels started successfully")
        
        # Step 7: Read data from RX channels
        print("7. Reading data from RX channels for 10 seconds...")
        print("   Press Ctrl+C to stop early")
        print()
        
        start_time = time.time()
        read_count = 0
        
        while time.time() - start_time < 10:
            for channel in [CHANNEL_VHF_BUS, CHANNEL_NAV_BUS, CHANNEL_UHF_BUS, CHANNEL_AMURX_BUS]:
                if channel in rx_channels_config:
                    data = read_channel_data(
                        rx_channels_config[channel]['handle'] if USE_HARDWARE else None, 
                        channel, 
                        max_words=5
                    )
                    if data:
                        read_count += len(data)
                        for entry in data:
                            channel_name = get_channel_name(channel)
                            print(f"{channel_name}: {entry['timestamp']} - Label {entry['label']} - Data 0x{entry['data']} - {entry.get('description', '')}")
            
            time.sleep(0.5)  # Small delay between reads
        
        print(f"✅ Read {read_count} data words total")
        
        # Step 8: Clean up
        print("8. Cleaning up resources...")
        
        if USE_HARDWARE:
            # Stop TX channels
            for channel, config in tx_channels.items():
                owl429.Owl429_ITxRateOrientedChannel_stop(config['handle'])
                print(f"✅ TX Channel {channel} stopped")
            
            # Stop RX channels
            for channel, config in rx_channels_config.items():
                owl429.Owl429_IRxChronMonChannel_stop(config['handle'])
                print(f"✅ RX Channel {channel} stopped")
            
            # Release board
            owl429.Owl429_IBoard_release(byref(board))
            print("✅ Board released")
            
            # Release factory
            owl429.Owl429_IBoardFactory_release(byref(factory))
            print("✅ Factory released")
        else:
            print("✅ Emulated resources cleaned up")
        
        print("✅✅✅ ARINC-429 TEST COMPLETED SUCCESSFULLY ✅✅✅")
        return True
        
    except KeyboardInterrupt:
        print("\n🛑 Test interrupted by user")
        return False
    except Exception as e:
        print(f"\n❌ Test failed with error: {str(e)}")
        import traceback
        traceback.print_exc()
        return False

def configure_tx_channel(board, channel_number, label, data, transfer_period_ms, speed=12500):
    """Configure a TX channel"""
    try:
        if USE_HARDWARE:
            # Create TX label configuration
            label_config = hOwl429TxScheduledLabelConfig()
            rc = owl429.Owl429_TxScheduledLabelConfig_new(byref(label_config), label)
            if rc != OWL429_OK:
                print(f"Failed to create label config: {get_error_message()}")
                return None
            
            # Configure transfer period
            rc = owl429.Owl429_TxScheduledLabelConfig_setTransferPeriod(label_config, transfer_period_ms)
            if rc != OWL429_OK:
                print(f"Failed to set transfer period: {get_error_message()}")
                return None
            
            # Set SSM (Status/Status Matrix)
            arinc_word = c_uint32(0)
            rc = owl429.Owl429_ArincUtil_setSsm(0, 3, byref(arinc_word))  # Set SSM to 3 (Normal Operation)
            if rc != OWL429_OK:
                print(f"Failed to set SSM: {get_error_message()}")
                return None
            
            # Set the unsigned BNR data
            rc = owl429.Owl429_ArincUtil_setUnsignedBnrData(arinc_word.value, data, byref(arinc_word))
            if rc != OWL429_OK:
                print(f"Failed to encode ARINC data: {get_error_message()}")
                return None
            
            # Set data into the config
            rc = owl429.Owl429_TxScheduledLabelConfig_setDatum(label_config, arinc_word.value, 1)
            if rc != OWL429_OK:
                print(f"Failed to set ARINC data: {get_error_message()}")
                return None
            
            # Create TX rate oriented configuration
            tx_config = hOwl429TxRateOrientedConfig()
            rc = owl429.Owl429_TxRateOrientedConfig_new(byref(tx_config))
            if rc != OWL429_OK:
                print(f"Failed to create TX config: {get_error_message()}")
                return None
            
            # Configure TX speed
            rc = owl429.Owl429_TxRateOrientedConfig_setSpeed(tx_config, speed)
            if rc != OWL429_OK:
                print(f"Failed to set TX speed: {get_error_message()}")
                return None
            
            # Add label to TX config
            transfer_handle = c_uint32(0)
            rc = owl429.Owl429_TxRateOrientedConfig_addTransferScheduled(
                tx_config,
                label_config,
                byref(transfer_handle)
            )
            if rc != OWL429_OK:
                print(f"Failed to add label to TX config: {get_error_message()}")
                return None
            
            # Configure TX channel
            tx_channel_handle = hOwl429ITxRateOrientedChannel()
            rc = owl429.Owl429_IBoard_configureChannelTxRate(
                board,
                channel_number,
                tx_config,
                byref(tx_channel_handle))
            
            if rc != OWL429_OK:
                print(f"TX channel configuration failed: {get_error_message()}")
                return None
            
            # Clean up configurations
            owl429.Owl429_TxRateOrientedConfig_delete(byref(tx_config))
            owl429.Owl429_TxScheduledLabelConfig_delete(byref(label_config))
            
            return {'handle': tx_channel_handle}
        else:
            # Emulation mode - just return a dummy handle
            return {'handle': None}
        
    except Exception as e:
        print(f"Error configuring TX channel {channel_number}: {str(e)}")
        return None

def configure_rx_channel(board, channel_number, speed=12500):
    """Configure an RX channel"""
    try:
        if USE_HARDWARE:
            # Create RX configuration
            rx_config = hOwl429RxChronMonConfig()
            rc = owl429.Owl429_RxChronMonConfig_new(byref(rx_config))
            if rc != OWL429_OK:
                print(f"Failed to create RX config: {get_error_message()}")
                return None
            
            # Configure RX speed
            rc = owl429.Owl429_RxChronMonConfig_setSpeed(rx_config, speed)
            if rc != OWL429_OK:
                print(f"Failed to set RX speed: {get_error_message()}")
                return None
            
            # Configure RX channel
            rx_channel_handle = hOwl429IRxChronMonChannel()
            rc = owl429.Owl429_IBoard_configureChannelRx(
                board,
                channel_number,
                rx_config,
                byref(rx_channel_handle))
            
            if rc != OWL429_OK:
                print(f"RX channel configuration failed: {get_error_message()}")
                return None
            
            # Clean up configuration
            owl429.Owl429_RxChronMonConfig_delete(byref(rx_config))
            
            return {'handle': rx_channel_handle}
        else:
            # Emulation mode - just return a dummy handle
            return {'handle': None}
        
    except Exception as e:
        print(f"Error configuring RX channel {channel_number}: {str(e)}")
        return None

def read_channel_data(rx_channel_handle, channel_number, max_words=50):
    """Read data from an RX channel"""
    try:
        if USE_HARDWARE:
            # Create a read buffer
            read_buffer = hOwl429ReadBuffer()
            rc = owl429.Owl429_ReadBuffer_new(byref(read_buffer))
            if rc != OWL429_OK:
                print(f"Could not create read buffer: {get_error_message()}")
                return []
            
            words_read = c_uint32(0)
            # Read all available data
            rc = owl429.Owl429_IRxChronMonChannel_readReadBuffer(
                rx_channel_handle,
                read_buffer,
                max_words,
                byref(words_read))
            
            if rc != OWL429_OK:
                print(f"Error reading data: {get_error_message()}")
                owl429.Owl429_ReadBuffer_delete(byref(read_buffer))
                return []
            
            buffer_size = c_uint32(0)
            rc = owl429.Owl429_ReadBuffer_size(read_buffer, byref(buffer_size))
            if rc != OWL429_OK:
                print("Could not get buffer size")
                owl429.Owl429_ReadBuffer_delete(byref(read_buffer))
                return []
            
            results = []
            for i in range(buffer_size.value):
                entry = hOwl429IBufferEntry()
                rc = owl429.Owl429_ReadBuffer_getEntry(read_buffer, i, byref(entry))
                if rc != OWL429_OK:
                    print(f"Could not get buffer entry {i}")
                    continue
                
                # Get timestamp
                time_tag = Owl429TimeTag()
                rc = owl429.Owl429_IBufferEntry_getTimeTag(entry, byref(time_tag))
                if rc != OWL429_OK:
                    print(f"Could not get timestamp for entry {i}")
                    continue
                
                # Get data word
                word = c_uint32(0)
                rc = owl429.Owl429_IBufferEntry_getArincDataWord(entry, byref(word))
                if rc != OWL429_OK:
                    print(f"Could not get ARINC word for entry {i}")
                    continue
                
                # Extract label and data from ARINC word
                label = (word.value >> 24) & 0xFF
                data = word.value & 0xFFFFF
                
                # Format the result
                result = {
                    'timestamp': f"{time_tag.hour:02d}:{time_tag.minute:02d}:{time_tag.second:02d}:{time_tag.microSecond:06d}",
                    'raw_word': f"{word.value:08X}",
                    'label': label,
                    'data': f"{data:05X}",
                    'decimal_data': data,
                    'channel': channel_number
                }
                
                results.append(result)
            
            owl429.Owl429_ReadBuffer_delete(byref(read_buffer))
            return results
        else:
            # Emulation mode - generate fake data
            results = []
            now = datetime.now()
            
            # Generate 1-3 random data entries
            num_entries = random.randint(1, 3)
            for _ in range(num_entries):
                # Select random data from our emulation data
                if channel_number in EMULATION_DATA and EMULATION_DATA[channel_number]:
                    data_entry = random.choice(EMULATION_DATA[channel_number])
                    
                    # Add some random variation to the data
                    varied_data = data_entry['data'] + random.randint(-100, 100)
                    
                    result = {
                        'timestamp': now.strftime("%H:%M:%S:%f")[:-3],  # Truncate to milliseconds
                        'raw_word': f"{varied_data:08X}",
                        'label': data_entry['label'],
                        'data': f"{varied_data:05X}",
                        'decimal_data': varied_data,
                        'channel': channel_number,
                        'description': data_entry['description']
                    }
                    
                    results.append(result)
            
            return results
        
    except Exception as e:
        print(f"Error reading data from channel {channel_number}: {str(e)}")
        return []

def get_channel_name(channel_number):
    """Get the name of a channel from its number"""
    channel_names = {
        CHANNEL_RADIO_BUS: "RADIO_BUS",
        CHANNEL_VHF_BUS: "VHF_BUS",
        CHANNEL_NAV_BUS: "NAV_BUS",
        CHANNEL_UHF_BUS: "UHF_BUS",
        CHANNEL_AMUTX_BUS: "AMUTX_BUS",
        CHANNEL_AMURX_BUS: "AMURX_BUS"
    }
    return channel_names.get(channel_number, f"UNKNOWN_{channel_number}")

if __name__ == "__main__":
    # Check if user wants to run in hardware mode
    if len(sys.argv) > 1 and sys.argv[1].lower() in ['-h', '--hardware', 'hardware']:
        USE_HARDWARE = True
        print("Running in HARDWARE mode")
    else:
        print("Running in EMULATION mode (use -h argument for hardware mode)")
    
    # Run the test
    success = test_arinc429_device()
    
    if success:
        print("\n🎉 Test completed successfully!")
        if not USE_HARDWARE:
            print("   You can now test with hardware by running: python test_arinc429.py -h")
    else:
        print("\n💥 Test failed.")
        if USE_HARDWARE:
            print("   Please check your hardware connections and DLL installation.")
        else:
            print("   Emulation mode error - please check the code.")
    
    print("\nPress Enter to exit...")
    input()